//
//  Client.m
//  tcpoctest
//
//  Created by gump on 10/18/12.
//  Copyright (c) 2012 gump. All rights reserved.
//

#import "Client.h"

#import <netdb.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <pthread.h>
#import <fcntl.h>

#import "PromptLoginDataSource.h"

StreamBuffer    *sb = nil;

void looprecv(Client *client)
{
    [client initializeSocket];
    
    struct sockaddr_in inAddr;
    int len = sizeof(struct sockaddr_in);
    memset(&inAddr, 0x00, len);
    inAddr.sin_family = AF_INET;
    inAddr.sin_port = htons([client getPort]);
    inAddr.sin_len = len;
    inAddr.sin_addr.s_addr = inet_addr([client getIP]);
    
    int flags = fcntl([client getSocketHandle], F_GETFL);
    fcntl([client getSocketHandle], F_SETFL, flags|O_SYNC);
    
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET([client getSocketHandle], &rfds);
    struct timeval time;
    time.tv_sec = 1;
    time.tv_usec = 0;
    
    int result = connect([client getSocketHandle], (struct sockaddr *)&inAddr, sizeof(inAddr));
    
    N_LOG(@"connect handle %d result is %d",[client getSocketHandle],result);
    
    if (result<0)
    {
            N_LOG(@"wocao connect unusual break");
            
            [client SetCloseSocket:true];
            [client SetIsReConnect:false];
        if ([DataControl shareControl].iGameState != GMS_CHANGEGROUND) {
            
            [[DataControl shareControl].UICommendDS addUICommand:UC_WARNINGSERVER];
            N_LOG(@"wocao socket close gamestate %d",
                  [DataControl shareControl].iGameState);
        }
            
            return;
    }
    else
    {
        N_LOG(@"connect success 1");
        
        [client SetCloseSocket:false];
        if ([client IsReConnect]) {
            
            N_LOG(@"relogin");
            
            [[DataControl shareControl].promptloginDS SetIsPromptLogin:false];
            [[DataControl shareControl].promptloginDS SetIsShowMain:false];
            [[DataControl shareControl].promptloginDS SetIsInitData:false];
            [[DataControl shareControl].LCDS addLogicCommand:LC_LOGINAUTO];
            
            [client SetIsReConnect:false];
        }
    }
    
    int bufflen = BUFFER_LENGTH;
    setsockopt([client getSocketHandle], SOL_SOCKET, SO_RCVBUF, (const char *)&bufflen, sizeof(int));
    bufflen = BUFFER_LENGTH;
    setsockopt([client getSocketHandle], SOL_SOCKET, SO_SNDBUF, (const char *)&bufflen, sizeof(int));
    
    memset([client getBuffer], 0, BUFFER_LENGTH);
    
    while (true)
    {
        if ([client IsCloseSocket]) {
            [client CloseSocket];
            
            [DataControl shareControl].blogin = false;
            
            if ([client IsReConnect]) {
                [[DataControl shareControl].LCDS addLogicCommand:LC_NET_RECONNECT];
                N_LOG(@"wocao socket close and reconnet");
                break;
            }
            else
            {
                if ([DataControl shareControl].iGameState != GMS_CHANGEGROUND) {
                    
                    [[DataControl shareControl].UICommendDS addUICommand:UC_WARNINGSERVER];
                    N_LOG(@"wocao socket close gamestate %d",
                          [DataControl shareControl].iGameState);
                }
                else
                {
                    N_LOG(@"wocao qiehoutai socket close gamestate %d",
                          [DataControl shareControl].iGameState);
                }
            }
            
            break;
        }
        
        time.tv_sec = 0;
        time.tv_usec = 100;
        
        FD_ZERO(&rfds);
        FD_SET([client getSocketHandle], &rfds);
        
        int selresult = select([client getSocketHandle] + 1, &rfds, NULL, NULL, &time);
        
        if(selresult > 0)
        {
            len = recv([client getSocketHandle], [client getBuffer], BUFFER_LENGTH, 0);
            if(len <= 0)
            {
                N_LOG(@"wocao recv unusual break");
                
                [client SetCloseSocket:true];
                [client SetIsReConnect:true];
                
                continue;
            }
            else
            {
                [sb Write:(unsigned char *)[client getBuffer] withSize:len];
                
                [client PushPacket:sb];
            }
        }
        
        [client SendPacket];
    }
}

@implementation Client

-(void)dealloc
{
    free(mBuffer);
    [sb Clean];
}

-(id)init
{
    self = [super init];
    if(self)
    {
        mPacketArr = [[NSMutableArray alloc]init];
        mSendPacketArr = [[NSMutableArray alloc]init];
        mip = "";
        mport = IP_PORT;
        mBuffer = (char *)malloc(BUFFER_LENGTH);
        assert(mBuffer != nil);
        sb = [[StreamBuffer alloc]init];
        mbcloseSocket = true;
        pthread_mutex_init(&mnetsendmutex, 0);
        pthread_mutex_init(&mnetrecivemutex, 0);
        mbreconnect = false;
    }
    return self;
}

-(bool)Connect:(char *)stradd withPort:(int)port
{
    mip = stradd;
    mport = port;
        
    pthread_t pthreadThreadDetails;
    void *(*func)(void *) = (void *(*)(void *))looprecv;
    pthread_create(&pthreadThreadDetails, NULL, func, (__bridge void *)(self));
    
    return true;
}

-(PACKET *)receive
{
    PACKET *p = (PACKET*)malloc(sizeof(PACKET));
    assert(p != nil);
    return p;
}

-(void)freePacket:(PACKET *)p
{
    if(p)
    {
        if (p->data)
        {
            free(p->data);
        }
        
        free(p);
    }
}

-(char*)getBuffer
{
    return mBuffer;
}
            
-(int)getSocketHandle
{
    return mSocketHandle;
}
            
-(void)PushPacket:(StreamBuffer *)sb
{
    while (true)
    {
        if ([sb ReadHead])
        {
            int value = [sb getReadValue];
            if ([sb getStreamSize] - HEAD_LENGTH >= value)
            {
                PACKET *p = (PACKET*)malloc(sizeof(PACKET));
                assert(p != nil);
                p->length = value;
                p->data = (unsigned char *)malloc(sizeof(char) * value);
                assert(p->data != nil);
                [sb Read];
                value = [sb getReadValue];
                if([sb Read:p->data withLength: value])
                {
                    pthread_mutex_lock(&mnetrecivemutex);
                    
                    NSValue *value = [NSValue valueWithBytes:p objCType:@encode(PACKET)];
                    [mPacketArr addObject:value];
                    
                    pthread_mutex_unlock(&mnetrecivemutex);
                }
                
            }
            else
            {
                break;
            }
        }
        else
        {
            break;
        }
    }
    
    [sb reSet];
    
}

-(PACKET*)getPopPacket
{
    pthread_mutex_lock(&mnetrecivemutex);
    
    if([mPacketArr count] == 0)
    {
        pthread_mutex_unlock(&mnetrecivemutex);
        return NULL;
    }
    else
    {
        NSValue *value = [mPacketArr objectAtIndex:0];
        PACKET *p = (PACKET *)malloc(sizeof(PACKET));
        assert(p != nil);
        [value getValue:p];
        N_LOG(@"poppacket length%d ",p->length);
        [mPacketArr removeObjectAtIndex:0];
        
        pthread_mutex_unlock(&mnetrecivemutex);
        
        return p;
    }
    
}

-(void)SendPacket
{
    pthread_mutex_lock(&mnetsendmutex);
    
    
    if ([mSendPacketArr count] == 0)
    {
        pthread_mutex_unlock(&mnetsendmutex);
        return;
    }
    
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    
    NSValue *value = [mSendPacketArr objectAtIndex:0];
    PACKET *p = (PACKET *)malloc(sizeof(PACKET));
    assert(p != nil);
    [value getValue:p];
    [mSendPacketArr removeObjectAtIndex:0];
    
    char *data = (char *)malloc(p->length + HEAD_LENGTH);
    assert(data != nil);
    int len = htonl(p->length);
    memcpy(data, &len, HEAD_LENGTH);
    memcpy(data + HEAD_LENGTH, p->data, p->length);
    
   
            
            if (p->length < SEND_SOCKET_MAX) {
                
                fd_set wfds;
                FD_ZERO(&wfds);
                FD_SET(mSocketHandle, &wfds);
                struct timeval time;
                time.tv_sec = 0;
                time.tv_usec = 100;
                int result = select(mSocketHandle+1, NULL, &wfds, NULL, &time);
                if (result > 0)
                {
                    if (FD_ISSET(mSocketHandle, &wfds))
                    {
                        
                        int irs = send(mSocketHandle, data, p->length + HEAD_LENGTH, 0);
                        N_LOG(@"send resoult %d should send %d handle is %d",irs,p->length + 4,mSocketHandle);
                        if (irs < 0)
                        {
                            N_LOG(@"wocao send unusual break");
                            [self SetCloseSocket:true];
                            [self SetIsReConnect:true];
                        }
                    }
                    else
                    {
                        N_LOG(@"send bad ");
                        [self SetCloseSocket:true];
                        [self SetIsReConnect:true];
                    }
                }
                else
                {
                    N_LOG(@"send bad ");
                    [self SetCloseSocket:true];
                    [self SetIsReConnect:true];
                }
            }
            else
            {
                int tatollength = p->length + HEAD_LENGTH;
                int sendlength = SEND_SOCKET_MAX;
                int startpos = 0;
                
                while (tatollength > 0) {
                    
                    fd_set wfds;
                    FD_ZERO(&wfds);
                    FD_SET(mSocketHandle, &wfds);
                    struct timeval time;
                    time.tv_sec = 0;
                    time.tv_usec = 100;
                    int result = select(mSocketHandle+1, NULL, &wfds, NULL, &time);
                    if (result > 0)
                    {
                        if (FD_ISSET(mSocketHandle, &wfds))
                        {
                    int irs = send(mSocketHandle, data + startpos, sendlength, 0);
                    N_LOG(@"send resoult %d should send %d handle is %d alweady send %d",
                          irs,sendlength,mSocketHandle,p->length + 4 - tatollength);
                        if (irs < 0) {
                            N_LOG(@"wocao send unusual break");
                            [self SetCloseSocket:true];
                            [self SetIsReConnect:true];
                        }
                        else{
                            tatollength -= sendlength;
                            startpos += sendlength;
                            sendlength = (tatollength > SEND_SOCKET_MAX) ? SEND_SOCKET_MAX:tatollength;
                            
                            usleep(500);
                        }
                        }
                        else
                        {
                            N_LOG(@"send bad ");
                            [self SetCloseSocket:true];
                            [self SetIsReConnect:true];
                        }
                    
                    }
                    else
                    {
                        N_LOG(@"send bad ");
                        [self SetCloseSocket:true];
                        [self SetIsReConnect:true];
                    }
                }
            
                
            }
            
            [self freePacket:p];
            free(data);
      
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    
    pthread_mutex_unlock(&mnetsendmutex);
}

-(void)Package:(char *)date withLength:(int)len
{
    pthread_mutex_lock(&mnetsendmutex);
    
    PACKET *p = (PACKET *)malloc(sizeof(PACKET));
    assert(p != nil);
    p->length = len;
    p->data = (unsigned char*)malloc(sizeof(char) * p->length);
    assert(p->data != nil);
    memcpy(p->data, date, p->length);
    NSValue *value = [NSValue valueWithBytes:p objCType:@encode(PACKET)];
    [mSendPacketArr addObject:value];
    
    free(date);
    
    pthread_mutex_unlock(&mnetsendmutex);
}

-(char *)getIP
{
    return mip;
}

-(int)getPort
{
    return mport;
}

-(void)initializeSocket
{
    mSocketHandle = socket(AF_INET, SOCK_STREAM, 0);
    
    [sb Clean];
}

-(void)PackageHeartBeat
{
    char *data = (char *)malloc(sizeof(char));
    assert(data != nil);
    *data = 1;
    [self Package:data withLength:1];
}

-(void)SendQuitDirectly:(char *)quitdate
{
    if (![DataControl shareControl].blogin) {
        return;
    }
    
    if(mSocketHandle == -1)
        return;
    
    pthread_mutex_lock(&mnetsendmutex);
    
    fd_set wfds;
    FD_ZERO(&wfds);
    FD_SET(mSocketHandle, &wfds);
    struct timeval time;
    time.tv_sec = 0;
    time.tv_usec = 100;
    int result = select(mSocketHandle+1, NULL, &wfds, NULL, &time);
    if (result > 0)
    {
        if (FD_ISSET(mSocketHandle, &wfds))
        {
            int irs = 0;
            irs = send(mSocketHandle, quitdate, CHAR_PL * 2 + HEAD_LENGTH, 0);
            N_LOG(@"send SendQuitDirectly %d ",irs);
        }
    }
    else
    {
        N_LOG(@"can not sendd !");
    }
    
    free(quitdate);
    
    pthread_mutex_unlock(&mnetsendmutex);
}

-(bool)IsCloseSocket
{
    return mbcloseSocket;
}

-(void)CloseSocket
{
    close(mSocketHandle);
    mSocketHandle = -1;
    
    pthread_mutex_lock(&mnetrecivemutex);
    [mPacketArr removeAllObjects];
    pthread_mutex_unlock(&mnetrecivemutex);
    
    pthread_mutex_lock(&mnetsendmutex);
    [mSendPacketArr removeAllObjects];
     pthread_mutex_unlock(&mnetsendmutex);
    
    [sb Clean];
    
   
}

-(void)reConnect
{
    if (mSocketHandle != -1) {
        close(mSocketHandle);
        mSocketHandle = -1;
    }
    
    pthread_mutex_lock(&mnetrecivemutex);
    [mPacketArr removeAllObjects];
    pthread_mutex_unlock(&mnetrecivemutex);
    
    pthread_mutex_lock(&mnetsendmutex);
    [mSendPacketArr removeAllObjects];
    pthread_mutex_unlock(&mnetsendmutex);
    
    pthread_t pthreadThreadDetails;
    void *(*func)(void *) = (void *(*)(void *))looprecv;
    pthread_create(&pthreadThreadDetails, NULL, func, (__bridge void *)(self));
}

-(void)SetCloseSocket:(bool)isclose
{
    pthread_mutex_lock(&mnetsendmutex);
    
    mbcloseSocket = isclose;
    
    pthread_mutex_unlock(&mnetsendmutex);
}

-(void)SetIsReConnect:(bool)isconnect
{
    pthread_mutex_lock(&mnetsendmutex);
    
    mbreconnect = isconnect;
    
    pthread_mutex_unlock(&mnetsendmutex);
}

-(bool)IsReConnect
{
    return mbreconnect;
}

@end
